﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using TruyenTranhCrawler.Data;
using TruyenTranhCrawler.Utils;
using log4net;

namespace TruyenTranhCrawler
{
    public class BlogTruyen
    {
        private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        #region ctor
        private static readonly HtmlParser _parser = new HtmlParser();
        private static class Singleton
        {
            public static readonly BlogTruyen Instance = new BlogTruyen();
        }
        public static BlogTruyen Instance
        {
            get
            {
                return Singleton.Instance;
            }
        }
        #endregion

        #region Config

        private const string DomainLink = "http://blogtruyen.com";
        const string UrlPost = DomainLink + "/ListStory/GetListStory";
        const string FormatData = "Url=tatca&OrderBy=1&PageIndex={0}";

        const string OpenBookType = "<ul class=\"submenu category";
        const string CloseBookType = "</ul>";

        const string OpenTag = "<section id=\"main-left\"";
        const string CloseTag = "</section>";

        const string OpenInfo = "<div class=\"description";
        const string CloseInfo = "</div>";

        const string OpenListChap = "<div class=\"list-wrap\" id=\"list-chapters";
        const string CloseListChap = "</div>";

        const string OpenTagDescipt = "<div class=\"content";
        const string CloseTagDescript = "</div>";
        #endregion

        public void GetBookTypes()
        {
            Logger.Info("*** bat dau lay the loai truyen...");
            var sources = Process.GetHtmlSources(DomainLink + "/rss");
            if (sources.Length > 0)
            {
                var ss = _parser.GetContentInNode(sources, OpenBookType, CloseBookType);
                if (ss.Count > 0)
                {
                    var items = _parser.GetBetweenTag(ss[0], "li");
                    foreach (Match m in items)
                    {
                        try
                        {
                            var bt = new HistoryType { FullName = HttpUtility.HtmlDecode(_parser.RemoveAllTag(m.Value).Trim()) };
                            bt.InsertSkipExisted();
                            Logger.Info("Insert/Update: " + bt.FullName);
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Lay thong tin truyen moi
        /// </summary>
        public List<Book> GetNewBooks()
        {
            var results = new List<Book>();
            Logger.Info("*** Lay tat ca truyen moi");
            int total = 0;
            string currCrawlUrl = "", lastCrawlUrl = "";

            int page = 0;
            while (true)
            {
                //kiem tra khi het trang, du lieu se giong voi trang cuoi cung
                if (lastCrawlUrl.Length > 0 && currCrawlUrl == lastCrawlUrl) break;
                lastCrawlUrl = currCrawlUrl;

                page++;
                var sources = Process.PostHtmlSources(UrlPost, string.Format(FormatData, page));
                if (sources.Length > 0)
                {
                    sources = _parser.Replace(sources, HtmlParser.Patterns.BREAK, " ");
                    var matchs = _parser.GetBetweenTag(sources, "a");
                    foreach (Match m in matchs)
                    {
                        try
                        {
                            var urls = _parser.GetProperty(m.Value, "href");

                            if (urls.Count > 0)
                            {
                                var crawlUrl = urls[0].Groups[4].Value.Trim().Replace(" ", "");
                                if (_parser.Matched(crawlUrl, "javascript")) continue;
                                crawlUrl = Process.FillAbsoluteLink(DomainLink, crawlUrl);

                                //anchor current crawlurl
                                currCrawlUrl = crawlUrl;

                                var titles = _parser.GetProperty(m.Value, "title");
                                var fullName = HttpUtility.HtmlDecode(_parser.Replace(titles[0].Groups[4].Value, "truyện tranh", "")).Trim();
                                var book = new Book { CrawlHost = "blogtruyen", CrawlUrl = crawlUrl, FullName = fullName, CrawlName = fullName };
                                if (Book.IsNotExisted(book.CrawlUrl))
                                {
                                    var checker = results.Find(b => b.CrawlUrl == book.CrawlUrl);
                                    if (checker == null)
                                    {
                                        results.Add(book);
                                        total++;
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex);
                        }
                    }
                }
                else
                {
                    break;
                }
            }
            Logger.Info(string.Format("xong list new book: {0}", total));

            return results;
        }

        /// <summary>
        /// Xem page 1, neu co truyen moi thi bat dau lay het cac page con lai. ko thi bo qua
        /// </summary>
        /// <returns></returns>
        public List<Book> GetUpdatedBooks()
        {
            var results = new List<Book>();
            Logger.Info("*** kiem tra xem co book nao moi hoac updated hay khong");
            int total = 0;

            int page = 0;
            while (true)
            {
                var isUpdatedInPage = false;
                page++;
                var sources = Process.GetHtmlSources(string.Format("http://blogtruyen.com/trangchu/page/{0}", page));
                if (sources.Length > 0)
                {
                    sources = _parser.Replace(sources, HtmlParser.Patterns.BREAK, " ");
                    var matchs = _parser.GetBetweenTag(sources, "section");
                    foreach (Match m in matchs)
                    {
                        if (_parser.Matched(m.Value, "storyitem"))
                        {
                            try
                            {
                                var blocks = _parser.GetContentInNode(m.Value, "<div class=\"fl-l", "</div>");
                                if (blocks.Count > 0)
                                {
                                    var s = blocks[0];
                                    var crawlUrl = _parser.GetProperty(m.Value, "href")[0].Groups[4].Value.Trim();
                                    crawlUrl = HttpUtility.HtmlDecode(Process.FillAbsoluteLink(DomainLink, crawlUrl));

                                    var fullName = HttpUtility.HtmlDecode(_parser.GetProperty(m.Value, "title")[0].Groups[4].Value.Trim());
                                    var book = new Book
                                    {
                                        CrawlHost = "blogtruyen",
                                        CrawlUrl = crawlUrl,
                                        FullName = fullName,
                                        CrawlName = fullName,
                                        Image = Process.GetFirstImg(s)
                                    };
                                    //get total chap
                                    var newtotal = 0;
                                    blocks = _parser.GetContentInNode(m.Value, "<span class=\"count", "</span>");
                                    if (blocks.Count > 0)
                                    {
                                        foreach (var ss in blocks)
                                        {
                                            if (_parser.Matched(ss, "chương"))
                                            {
                                                var clear = _parser.RemoveAllTag(ss);
                                                clear = _parser.Replace(clear, "(chương)", "").Trim();
                                                int.TryParse(clear, out newtotal);
                                                break;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        Logger.Info("Khong lay duoc total chap de sosanh, hay vao kiem tra CODE: <span class=\"count\">...</span>");
                                    }
                                    if (Book.CanbeCrawl(ref book))
                                    {
                                        if (newtotal > book.TotalChap)
                                        {
                                            isUpdatedInPage = true;
                                            var checker = results.Find(b => b.CrawlUrl == book.CrawlUrl);
                                            if (checker == null)
                                            {
                                                results.Add(book);
                                                total++;
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                Logger.Error(ex);
                            }
                        }
                    }
                    //Logger.Info(string.Format("    xong trang {0}, tong cong {1} books new updated.", page, total));
                    if (!isUpdatedInPage)
                        break;
                }
                else
                {
                    break;
                }
            }
            Logger.Info(string.Format("xong new updated: {0}", total));

            return results;
        }
        /// <summary>
        /// Lay tat ca thong tin cua Book
        /// </summary>
        public int GetNewChaps(Book b)
        {
            int newcount = 0;
            var sources = Process.GetHtmlSources(b.CrawlUrl);
            if (sources.Length > 0)
            {
                sources = _parser.Replace(sources, HtmlParser.Patterns.BREAK, " ");
                var ss = _parser.GetContentInNode(sources, OpenTag, CloseTag);
                if (ss.Count > 0)
                {
                    var s = ss[0];
                    try
                    {
                        var isChanged = false;
                        if (string.IsNullOrEmpty(b.Image) || b.Image.IndexOf("18.jpg") > 0)
                        {
                            b.Image = Process.GetFirstImg(s);
                            isChanged = true;
                        }

                        if (string.IsNullOrEmpty(b.Description))
                        {
                            //get description
                            var descs = _parser.GetContentInNode(s, OpenTagDescipt, CloseTagDescript);
                            if (descs.Count > 0)
                            {
                                var desc = _parser.RemoveAllTag(descs[0]);
                                if (desc.Length > 0)
                                {
                                    b.Description = _parser.Replace(desc, "sơ lược", "").Trim();
                                    b.Description = _parser.RemoveTagOnly(b.Description, new string[] { "a", "script", "span" });
                                    isChanged = true;
                                }
                            }
                        }

                        //get tac gia, the loai, nguon,thoi gian cap nhat,nguoi dang, status
                        var infos = _parser.GetContentInNode(s, OpenInfo, CloseInfo);
                        if (infos.Count > 0)
                        {
                            var allElem = _parser.GetBetweenTag(infos[0], "p");
                            foreach (Match m in allElem)
                            {
                                var cleartext = HttpUtility.HtmlDecode(_parser.RemoveAllTag(m.Value).Replace(":", ""));

                                if (_parser.Matched(cleartext, "(tác giả|Author)"))
                                {
                                    if (string.IsNullOrEmpty(b.CrawlAuthor))
                                    {
                                        b.CrawlAuthor = _parser.Replace(cleartext, "(tác giả|Author)", "").Trim();
                                        isChanged = true;
                                    }
                                }
                                else if (_parser.Matched(cleartext, "(tên khác|other name)"))
                                {
                                    if (string.IsNullOrEmpty(b.OtherName))
                                    {
                                        b.OtherName = _parser.Replace(cleartext, "(tên khác|other name)", "").Trim();
                                        isChanged = true;
                                    }
                                }
                                else if (_parser.Matched(cleartext, "(nguồn|source)"))
                                {
                                    if (string.IsNullOrEmpty(b.Source))
                                    {
                                        b.Source = _parser.Replace(cleartext, "(nguồn|source)", "").Trim();
                                        isChanged = true;
                                    }
                                }
                                else if (_parser.Matched(cleartext, "(thể loại|types)"))
                                {
                                    if (string.IsNullOrEmpty(b.Types))
                                    {
                                        var alltags = _parser.GetBetweenTag(m.Value, "a");
                                        if (alltags.Count > 0)
                                        {
                                            var types = alltags.Cast<Match>().Aggregate("", (current, match) => current + (match.Groups[3].Value + ","));
                                            types = types.Remove(types.Length - 1);
                                            b.Types = types;
                                            isChanged = true;
                                        }
                                    }
                                }
                                else if (_parser.Matched(cleartext, "(trạng thái|status)"))
                                {
                                    if (!b.Finish && _parser.Matched(cleartext, "(hoàn thành|đủ bộ|full)"))
                                    {
                                        b.Finish = true;
                                        isChanged = true;
                                    }
                                }
                            }
                        }

                        if (b.ID == 0)
                        {
                            b.Save();
                        }
                        else if (isChanged)
                        {
                            b.Update();
                        }
                        var ccl = new CommonCrawler();

                        //get list chapter
                        var allExisted = Content.FindByBook(b.ID).Select(x => x.CrawlUrl).ToList();

                        var blocklistchap = _parser.GetContentInNode(s, OpenListChap, CloseListChap);
                        if (blocklistchap.Count > 0)
                        {
                            var allChapFromSource = _parser.GetBetweenTag(blocklistchap[0], "p");
                            //int totalFromSource = allChapFromSource.Count;
                            int nextChapter = allExisted.Count;
                            var newLastChapId = 0;
                            for (int i = allChapFromSource.Count - 1; i >= 0; i--)
                            {
                                nextChapter++;
                                var text = HttpUtility.HtmlDecode(allChapFromSource[i].Value);
                                var chap = new Content { Book_ID = b.ID, Chapter = nextChapter, SafeNameBook = b.SafeName };
                                var tagAs = _parser.GetBetweenTag(text, "a");
                                if (tagAs.Count > 0)
                                {
                                    var urls = _parser.GetProperty(tagAs[0].Value, "href");
                                    if (urls.Count > 0)
                                    {
                                        var crawlUrl = urls[0].Groups[4].Value.Trim();
                                        chap.CrawlUrl = Process.FillAbsoluteLink(DomainLink, crawlUrl);
                                    }
                                    chap.Title = _parser.RemoveAllTag(tagAs[0].Value).Trim();
                                }
                                if (!allExisted.Contains(chap.CrawlUrl))
                                {
                                    allExisted.Add(chap.CrawlUrl);//add into existed
                                    chap.Body = ccl.GetChapBody(chap.CrawlUrl);
                                    chap.Broken = chap.Body.Length == 0;
                                    chap.Save();
                                    newLastChapId = chap.Content_ID;

                                    newcount++;
                                }
                            }

                            //update last chapId for book
                            if (newLastChapId > 0)
                                Book.UpdateLastChap(b.ID, newLastChapId, allExisted.Count);

//                            var allChapFromSource = _parser.GetBetweenTag(blocklistchap[0], "p");
//                            int totalFromSource = allChapFromSource.Count;
//                            if (totalFromSource > allExisted.Count)
//                            {
//                                var lastChapId = 0;
//                                var idx = 1;
//                                //(index =0 la chuong moi nhat)
//                                while (idx <= totalFromSource)
//                                {
//                                    var text = HttpUtility.HtmlDecode(allChapFromSource[totalFromSource - idx].Value);                                    
//                                    var tagAs = _parser.GetBetweenTag(text, "a");
//                                    if (tagAs.Count > 0)
//                                    {
//                                        var urls = _parser.GetProperty(tagAs[0].Value, "href");
//                                        if (urls.Count > 0)
//                                        {
//                                            var chap = new Content { Book_ID = b.ID, Chapter = allExisted.Count + 1, SafeNameBook = b.SafeName };
//                                            var crawlUrl = urls[0].Groups[4].Value.Trim();
//                                            chap.CrawlUrl = Process.FillAbsoluteLink(DomainLink, crawlUrl);
//
//                                            if (!allExisted.Contains(chap.CrawlUrl))
//                                            {
//                                                allExisted.Add(chap.CrawlUrl); //add into existed
//
//                                                chap.Title = _parser.RemoveAllTag(tagAs[0].Value).Trim();
//                                                chap.Body = ccl.GetChapBody(chap.CrawlUrl);
//                                                chap.Broken = chap.Body.Length == 0;
//                                                chap.Save();
//                                                lastChapId = chap.Content_ID;
//
//                                                newcount++;
//                                            }
//                                        }
//                                    }
//                                    
//                                    idx++;
//                                }
//                                if (lastChapId > 0)
//                                    Book.UpdateLastChap(b.ID, lastChapId, allExisted.Count);
//                            }
                        }
                        if(newcount > 0)
                            Logger.Info(string.Format("New chap for book ID:{0}, sortname:{1}, finish: {2}, total chap:{3}", b.ID, b.SortName, b.Finish, newcount));
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex);
                    }
                }
            }
            return newcount;
        }
    }
}

